package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

// 1、看下面一段程序，回答问题
// s1 := make([]int, 3, 4)
// s2 := append(s1, 1)
// 请问s1、s2内各有什么元素？
// s1修改一个元素会影响s2吗？s2修改一个元素会影响s1吗？
// s2再增加一个元素会怎么样？
func f1() {
	s1 := make([]int, 3, 4)
	s2 := append(s1, 1)

	//1.请问s1、s2内各有什么元素？
	//s1元素：[0 0 0]
	//s2元素：[0 0 0 1]
	//注意：s1与s2两个切片的后台数组是同一个。
	fmt.Printf("s1: %v  %p\n", s1, &s1[0]) //s1: [0 0 0]  0xc000012180
	fmt.Printf("s2: %v  %p\n", s2, &s2[0]) //s2: [0 0 0 1]  0xc000012180

	//2.s1修改一个元素会影响s2吗？s2修改一个元素会影响s1吗？
	//会影响。因为二者的后台数组是同一个。
	//(1)修改s1
	s1[0] = 100
	fmt.Println(s1, s2) //[100 0 0] [100 0 0 1]
	//(2)修改s2
	s2[1] = 200
	fmt.Println(s1, s2) //[100 200 0] [100 200 0 1]

	//	3.s2再增加一个元素会怎么样？
	//s2再增加一个元素，会让s2扩容，后台数组变成新数组（因为cap==len==4 已经满了，再增加元素扩容）
	//fmt.Println(s2, len(s2), cap(s2)) //[100 200 0 1] 4 4
	s2 = append(s2, 500)
	fmt.Printf("s2: %v  %p\n", s2, &s2[0]) //s2: [100 200 0 1 500]  0xc000018280（后台数组地址已变）
}

// 2、有一个数组 [1,4,9,16,2,5,10,15]，生成一个新切片，要求新切片元素是数组相邻2项的和。
// 思路：（1）通过make创建空切片，容量为4；（2）若数组索引为偶数（0,2,4,6），则将该值与后一个值相加后，追加入切片中。
func f2() {
	arr := [...]int{1, 4, 9, 16, 2, 5, 10, 15}
	//fmt.Println(len(arr))
	//(1)创建空切片
	s := make([]int, 0, 4)
	//（2）为切片赋值：
	// 如果arr的索引为偶数，s追加元素（arr[i]+arr[i+1]）；反之arr的索引为奇数直接跳过本轮循环。
	for i := 0; i < len(arr); i++ {
		if i&1 == 0 { //如果i（索引）为偶数，则计算arr[i]+arr[i+1]后，追加入切片中
			s = append(s, arr[i]+arr[i+1])
		} else {
			continue
		}
	}
	//打印，比较原数组与切片
	fmt.Println("原数组：", arr)
	fmt.Println("新切片：", s, len(s), cap(s)) //查看新生成切片、长度与容量
}

// 3、数字重复统计
// 随机产生100个整数
// 数字的范围[-200, 200]
// 升序输出这些生成的数字并打印其重复的次数

// 思路：（1）生成随机数100个，并存入切片nums中，范围[-200,200)，可通过[0,400) - 200得到；
// （2）创建map m：map[int]int（随机数为key，重复次数为value）
// （3）将nums中的随机数作为key存放入map（m）中，重复次数为value；注意：需要判断m中是否已经有该key，如果有则value + 1（次数加1），反之，则value=1（次数为1）；
// （4）将map的key取出，追加入切片arr中，以方便为key排序；
// （5）对arr排序（即为key排序）
// （6）打印，查看随机数与重复次数（排序后）
func f3() {

	//(1) 生成随机数100个，范围[-200,200)
	//当前时间的纳秒值做随机数种子：r
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	//随机数: [0,400) - 200， 则随机数范围：[-200,200)
	var nums []int
	for i := 0; i < 100; i++ {
		nums = append(nums, r.Intn(400)-200)
	}
	//打印
	fmt.Println("随机数切片：", nums)
	fmt.Println()

	//(2) 创建map：key存放nums中的随机数，value存放随机数出现的次数；
	m := make(map[int]int, 100) //容量至多为100

	//（3）将随机数(nums)作为key放入map中，value为重复次数
	// 从nums[0]开始取随机数，首先判断nums keys中是否有该随机数，没有则添加key，并value = 1; 否则， value += 1;
	for i := 0; i < len(nums); i++ {
		if _, ok := m[nums[i]]; ok { //如果是true，则表示key中已有该值，value += 1
			m[nums[i]] += 1
		} else { //如果false，表示key中没有该值，values = 1
			m[nums[i]] = 1
		}
	}

	//（4）将key值取出存入切片中（方便排序）
	//需要将map的key值排序，需要先将key值放入切片，后对切片排序
	arr := make([]int, 0, 100)
	for k, _ := range m {
		arr = append(arr, k)
	}

	//fmt.Println("排序前：", arr, len(arr))
	//（5）排序
	sort.Ints(arr)
	//fmt.Println("排序后：", arr, len(arr))

	//（6）打印，查看随机数与重复次数
	//sum := 0
	for i := 0; i < len(arr); i++ {
		v := m[arr[i]]
		fmt.Printf("随机数%d：%d，出现次数：%d\n", i+1, arr[i], v)
		//sum += v
	}
	//fmt.Println(sum)
}

func main() {
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~习题1~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
	//习题1
	f1()

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~习题2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
	//习题2
	f2()

	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~~~习题3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
	//习题3
	f3()
}

// 批改意见
// 1. 第一题，s2修改最后一个元素不影响s1
// 2. 第二题，相邻元素之和是值1和2，2和3，3和4相加，不只是1和2，3和4相加
// 3. 随机数范围少了一个值，Intn的范围是[0, 400)，不包含400，因此-200后的范围是[-200,199]
